using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using Random = UnityEngine.Random;
#if UNITY_EDITOR
using UnityEditor;
#endif

namespace Gj
{
    public class ModelTools
    {
        public enum BlendMode
        {
            Opaque,
            Cutout,
            Fade,
            Transparent
        }

        public static Mesh gridMesh = CreateMesh(1, 1, 0);

        public static GameObject Create(string name)
        {
            var obj = new GameObject();
            obj.name = name;
            return obj;
        }

        public static GameObject Create(GameObject prefab)
        {
            var obj = Object.Instantiate(prefab);
            obj.name = prefab.name;
            return obj;
        }

        public static GameObject Create(GameObject prefab, GameObject parent, string name = null)
        {
            var obj = Object.Instantiate(prefab, parent.transform);
            obj.name = name == null ? prefab.name : name;
            return obj;
        }

        public static GameObject Create(GameObject prefab, Vector3 position, Quaternion rotation)
        {
            var obj = Object.Instantiate(prefab, position, rotation);
            obj.name = prefab.name;
            return obj;
        }

        public static GameObject Create(GameObject prefab, GameObject parent, Vector3 position, Quaternion rotation)
        {
            var obj = Object.Instantiate(prefab, parent.transform);
            obj.transform.position = position;
            obj.transform.rotation = rotation;
            obj.name = prefab.name;
            return obj;
        }

        public static GameObject CreateConnect(GameObject prefab)
        {
            return Connect(Create(prefab), prefab);
        }

        public static GameObject CreateConnect(GameObject prefab, GameObject parent, string name = null)
        {
            return Connect(Create(prefab, parent, name), prefab);
        }

        public static GameObject CreateConnect(GameObject prefab, Vector3 position, Quaternion rotation)
        {
            return Connect(Create(prefab, position, rotation), prefab);
        }

        public static GameObject Connect(GameObject prefab, GameObject target)
        {
#if UNITY_EDITOR
            return PrefabUtility.ConnectGameObjectToPrefab(target, prefab);
#else
            return null;
#endif
        }

        public static List<MeshRenderer> GetRenderers(GameObject obj)
        {
            var m = new List<MeshRenderer>();
            var r = obj.GetComponent<MeshRenderer>();
            if (r != null) m.Add(r);
            for (var i = 0; i < obj.transform.childCount; i++)
            {
                var item = obj.transform.GetChild(i).gameObject;
                if (!item.activeSelf) continue;
                if (item.transform.childCount > 0) m.AddRange(GetRenderers(item));
            }

            return m;
        }

        public static List<SkinnedMeshRenderer> GetSkinRenderers(GameObject obj)
        {
            var m = new List<SkinnedMeshRenderer>();
            var r = obj.GetComponent<SkinnedMeshRenderer>();
            if (r != null) m.Add(r);
            for (var i = 0; i < obj.transform.childCount; i++)
            {
                var item = obj.transform.GetChild(i).gameObject;
                if (!item.activeSelf) continue;
                m.AddRange(GetSkinRenderers(item));
            }

            return m;
        }

        public static Mesh CreateMesh(float length, float width, float height)
        {
            //vertices(顶点、必须):
            var vertices_count = 4 * 6; //顶点数（每个面4个点，六个面）
            var vertices = new Vector3[vertices_count];
            vertices[0] = new Vector3(length / 2 * -1, height / 2 * -1, width / 2 * -1); //前面的左下角的点
            vertices[1] = new Vector3(length / 2 * -1, height / 2, width / 2 * -1); //前面的左上角的点
            vertices[2] = new Vector3(length / 2, height / 2 * -1, width / 2 * -1); //前面的右下角的点
            vertices[3] = new Vector3(length / 2, height / 2, width / 2 * -1); //前面的右上角的点
            vertices[4] = new Vector3(length / 2, height / 2 * -1, width / 2); //后面的右下角的点
            vertices[5] = new Vector3(length / 2, height / 2, width / 2); //后面的右上角的点
            vertices[6] = new Vector3(length / 2 * -1, height / 2 * -1, width / 2); //后面的左下角的点
            vertices[7] = new Vector3(length / 2 * -1, height / 2, width / 2); //后面的左上角的点
            vertices[8] = vertices[6]; //左
            vertices[9] = vertices[7];
            vertices[10] = vertices[0];
            vertices[11] = vertices[1];
            vertices[12] = vertices[2]; //右
            vertices[13] = vertices[3];
            vertices[14] = vertices[4];
            vertices[15] = vertices[5];
            vertices[16] = vertices[1]; //上
            vertices[17] = vertices[7];
            vertices[18] = vertices[3];
            vertices[19] = vertices[5];
            vertices[20] = vertices[2]; //下
            vertices[21] = vertices[4];
            vertices[22] = vertices[0];
            vertices[23] = vertices[6];
            //triangles(索引三角形、必须):
            var 分割三角形数 = 6 * 2;
            var triangles_cout = 分割三角形数 * 3; //索引三角形的索引点个数
            var triangles = new int [triangles_cout]; //索引三角形数组
            for (int i = 0, vi = 0; i < triangles_cout; i += 6, vi += 4)
            {
                triangles[i] = vi;
                triangles[i + 1] = vi + 1;
                triangles[i + 2] = vi + 2;
                triangles[i + 3] = vi + 3;
                triangles[i + 4] = vi + 2;
                triangles[i + 5] = vi + 1;
            }

            //负载属性与mesh
            var mesh = new Mesh();
            mesh.vertices = vertices;
            mesh.triangles = triangles;
            // mesh.RecalculateBounds();
            // mesh.RecalculateNormals();
            // mesh.RecalculateTangents();
            return mesh;
        }

        public static void InitTrasform(Transform t)
        {
            t.localPosition = Vector3.zero;
            t.localRotation = Quaternion.identity;
        }

        public static void CopyTrasform(Transform t, Transform f)
        {
            t.localPosition = f.localPosition;
            t.localRotation = f.localRotation;
        }

        public static void RotateAngle(GameObject obj, float angle, float speed)
        {
            obj.transform.rotation = Quaternion.RotateTowards(obj.transform.rotation, Quaternion.Euler(0, angle, 0),
                Time.deltaTime * speed);
        }

        public static void RotateAngle(GameObject obj, float angle)
        {
            obj.transform.rotation = Quaternion.Euler(obj.transform.rotation.eulerAngles.x, angle,
                obj.transform.rotation.eulerAngles.z);
        }

        public static void AddTriggersListener(GameObject obj, EventTriggerType eventID,
            UnityAction<BaseEventData> action)
        {
            var trigger = obj.GetComponent<EventTrigger>();
            if (trigger == null) trigger = obj.AddComponent<EventTrigger>();

            if (trigger.triggers.Count == 0) trigger.triggers = new List<EventTrigger.Entry>();

            var callback = action;
            var entry = new EventTrigger.Entry();
            entry.eventID = eventID;
            entry.callback.AddListener(callback);
            trigger.triggers.Add(entry);
        }

        public static Color RandomColor()
        {
            var color = new Color();
            color.r = (float)Random.Range(0, 100) / 100;
            color.g = (float)Random.Range(0, 100) / 100;
            color.b = (float)Random.Range(0, 100) / 100;
            return color;
        }

        public static Material GetMaterial(GameObject obj)
        {
            return obj.GetComponent<Renderer>().material;
        }

        public static void ChangeMaterial(GameObject obj, Material material)
        {
            obj.GetComponent<Renderer>().material = material;
        }

        public static void ChangeColor(GameObject obj, Color color)
        {
            var material = GetMaterial(obj);
            color.a = material.color.a;
            material.color = color;
        }

        public static void ChangeAlpha(GameObject obj, float alpha)
        {
            var material = GetMaterial(obj);
            var color = material.color;
            color.a = alpha;
            material.color = color;
        }

        public static void ChangeRenderMode(GameObject obj, BlendMode blendMode)
        {
            var material = GetMaterial(obj);
            ChangeRenderMode(material, blendMode);
        }

        public static void ChangeAlpha(Material material, float alpha)
        {
            var color = material.color;
            color.a = alpha;
            material.color = color;
        }

        public static void Change(GameObject obj, string key, Color color)
        {
            var material = GetMaterial(obj);
            material.SetColor(key, color);
        }

        public static void ChangeRenderMode(Material material, BlendMode blendMode)
        {
            switch (blendMode)
            {
                case BlendMode.Opaque:
                    material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
                    material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
                    material.SetInt("_ZWrite", 1);
                    material.DisableKeyword("_ALPHATEST_ON");
                    material.DisableKeyword("_ALPHABLEND_ON");
                    material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                    material.renderQueue = -1;
                    break;
                case BlendMode.Cutout:
                    material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
                    material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
                    material.SetInt("_ZWrite", 1);
                    material.EnableKeyword("_ALPHATEST_ON");
                    material.DisableKeyword("_ALPHABLEND_ON");
                    material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                    material.renderQueue = 2450;
                    break;
                case BlendMode.Fade:
                    material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
                    material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
                    material.SetInt("_ZWrite", 0);
                    material.DisableKeyword("_ALPHATEST_ON");
                    material.EnableKeyword("_ALPHABLEND_ON");
                    material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                    material.renderQueue = 3000;
                    break;
                case BlendMode.Transparent:
                    material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
                    material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
                    material.SetInt("_ZWrite", 0);
                    material.DisableKeyword("_ALPHATEST_ON");
                    material.DisableKeyword("_ALPHABLEND_ON");
                    material.EnableKeyword("_ALPHAPREMULTIPLY_ON");
                    material.renderQueue = 3000;
                    break;
            }
        }

        public static float3 RGBToHSV(float3 rgb)
        {
            float R = rgb.x, G = rgb.y, B = rgb.z;
            var hsv = float3.zero;
            var max1 = math.max(R, math.max(G, B));
            var min1 = math.min(R, math.min(G, B));
            if (R == max1) hsv.x = (G - B) / (max1 - min1);
            if (G == max1) hsv.x = 2 + (B - R) / (max1 - min1);
            if (B == max1) hsv.x = 4 + (R - G) / (max1 - min1);
            hsv.x = hsv.x * 60.0f;
            if (hsv.x < 0)
                hsv.x = hsv.x + 360;
            hsv.z = max1;
            hsv.y = (max1 - min1) / max1;
            return hsv;
        }

        public static float3 HSVToRGB(float3 hsv)
        {
            float R, G, B;
            //float3 rgb;
            if (hsv.y == 0)
            {
                R = G = B = hsv.z;
            }
            else
            {
                hsv.x = hsv.x / 60.0f;
                var i = (int)hsv.x;
                var f = hsv.x - i;
                var a = hsv.z * (1 - hsv.y);
                var b = hsv.z * (1 - hsv.y * f);
                var c = hsv.z * (1 - hsv.y * (1 - f));
                switch (i)
                {
                    case 0:
                        R = hsv.z;
                        G = c;
                        B = a;
                        break;
                    case 1:
                        R = b;
                        G = hsv.z;
                        B = a;
                        break;
                    case 2:
                        R = a;
                        G = hsv.z;
                        B = c;
                        break;
                    case 3:
                        R = a;
                        G = b;
                        B = hsv.z;
                        break;
                    case 4:
                        R = c;
                        G = a;
                        B = hsv.z;
                        break;
                    default:
                        R = hsv.z;
                        G = a;
                        B = b;
                        break;
                }
            }

            return new float3(R, G, B);
        }
    }
}